#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// WiFi网络相关配置，直接在代码中定义可配置信息
const char* ssid = "rooconnect";  // 将此处替换为实际的WiFi名称
const char* password = "rooconnect";  // 将此处替换为实际的WiFi密码

// MQTT服务器相关配置，同样直接定义在代码中
const char* mqtt_server = "192.168.1.9";  // 将此处替换为实际的MQTT服务器地址
const int mqtt_port = 1883;

// 创建WiFi和MQTT客户端对象
WiFiClient espClient;
PubSubClient client(espClient);

// 连接继电器的引脚，这里假设使用ESP8266的GPIO0引脚，可根据实际调整
const int relayPin = 0;

// 假设设备名为ESP8266_Relay，可根据实际修改
const char* deviceName = "ESP8266_Relay"; 

// 最大WiFi连接重试次数
const int MAX_WIFI_RETRY = 1000;
// WiFi连接重试间隔（毫秒）
const int WIFI_RETRY_INTERVAL = 500;

// MQTT连接最大重试次数
const int MAX_MQTT_RETRY = 1000;
// MQTT连接初始重试间隔（毫秒），后续可根据失败次数调整
const int MQTT_RETRY_INITIAL_INTERVAL = 5000;

// 用于存储WiFi连接重试次数
int wifiRetryCount = 0;
// 用于存储MQTT连接重试次数
int mqttRetryCount = 0;

// 连接Wi-Fi网络，增加了错误处理和超时机制
void setup_wifi() {
    delay(10);
    WiFi.begin(ssid, password);
    while (WiFi.status()!= WL_CONNECTED && wifiRetryCount < MAX_WIFI_RETRY) {
        delay(WIFI_RETRY_INTERVAL);
        wifiRetryCount++;
    }
    if (WiFi.status() == WL_CONNECTED) {
        Serial.println("WiFi连接成功");
    } else {
        Serial.println("WiFi连接失败，请检查网络配置或设备状态");
    }
}

// MQTT消息回调函数，处理接收到的消息并控制继电器，同时按要求返回消息，优化了内存使用
void callback(char* topic, byte* payload, unsigned int length) {
    Serial.print("Message arrived [");
    Serial.print(topic);
    Serial.print("] ");
    char message[length + 1];  // 使用固定大小数组存储消息，避免String类的内存问题
    for (int i = 0; i < length; i++) {
        message[i] = (char)payload[i];
    }
    message[length] = '\0';
    Serial.println(message);

    if (strcmp(message, "1") == 0) {
        digitalWrite(relayPin, HIGH);
        Serial.println("Relay turned ON");
    } else if (strcmp(message, "0") == 0) {
        digitalWrite(relayPin, LOW);
        Serial.println("Relay turned OFF");
    } else {
        Serial.println("非法错误");
    }

    // 根据接收到消息的内容决定返回的消息内容
    char responseMessage[50];  // 根据可能的最长消息长度合理设置数组大小
    if (strcmp(message, "1") == 0 || strcmp(message, "0") == 0) {
        strcpy(responseMessage, message);
    } else {
        snprintf(responseMessage, sizeof(responseMessage), "非法消息 %s", deviceName);
    }
    client.publish("response_topic", responseMessage);
}

// 连接MQTT服务器，优化了重试逻辑
void reconnect() {
    while (!client.connected() && mqttRetryCount < MAX_MQTT_RETRY) {
        if (client.connect("ESP8266Client")) {  // 连接MQTT服务器
            Serial.println("MQTT连接成功");
            client.subscribe("jidianqi");  // 订阅主题
            mqttRetryCount = 0;  // 连接成功后重置重试次数
        } else {
            Serial.print("MQTT connection failed, rc=");
            Serial.print(client.state());
            // 调整下次重试间隔，例如每次失败后间隔翻倍（简单示例，可根据实际优化）
            int retryInterval = MQTT_RETRY_INITIAL_INTERVAL * (mqttRetryCount + 1);
            delay(retryInterval);
            mqttRetryCount++;
        }
    }
    if (mqttRetryCount >= MAX_MQTT_RETRY) {
        Serial.println("MQTT连接多次失败，可能服务器存在问题，请检查");
    }
}

void setup() {
    Serial.begin(115200);
    pinMode(relayPin, OUTPUT);
    digitalWrite(relayPin, LOW);  // 初始化为关闭继电器
    setup_wifi();  // 连接Wi-Fi
    client.setServer(mqtt_server, mqtt_port);  // 设置MQTT服务器
    client.setCallback(callback);  // 设置回调函数
}

// 在loop函数中适时检查引脚状态（简单示例，可根据实际完善检测逻辑）
void checkRelayPinStatus() {
    int currentPinState = digitalRead(relayPin);
    // 这里可以添加更多逻辑判断当前状态是否符合预期等情况
    // 例如可以和上一次记录的正确状态对比，如果不一致尝试纠正等
    Serial.print("当前继电器引脚状态: ");
    Serial.println(currentPinState == HIGH? "开启" : "关闭");
}

void loop() {
    if (!client.connected()) {
        reconnect();
    }
    client.loop();
    checkRelayPinStatus();  // 检查引脚状态，增加可靠性
}